哈工大 SCIR 实验室笔试小记

昨天参加了哈工大 SCIR 实验室的 2024 研究生招生笔试。试题很基础,但题量很大,一个半小时要完成一道逻辑题,一道文献翻译题,两道数学题,两道神经网络相关知识的题,两道编程题。反正我没做完。

本文给出两道数学题和两道编程题的题解,权当复习巩固基础。

一、高斯分布的 KL 散度

这道题完全空着了,忘了 KL 散度怎么求了。究其原因是没有深入理解信息熵、交叉熵、相对熵那一套原理 [1]

信息熵:

Hp(X)=p(x)log(p(x))dx

logb(p(x))=logb(1p(x)) 可以理解为一个事件的不确定性程度,那么 Hp(X) 显然就是整个分布的期望。

交叉熵:

Hpo,ps(X)=po(x)log(ps(x))dx

其中,ps 为主观认为的概率分布,或者说机器学习方法预测出来的概率分布,而 po 为客观的概率分布。可以理解为,我们带着某个主观认知去接触某个客观随机现象的不确定性程度。

相对熵(KL 散度):

DKL(po||ps)=Hpo,ps(X)Hpo(X)=po(x)log(ps(x)po(x))dx

其实就是衡量交叉熵与信息熵的差值。

KL 散度的若干条性质:

回到本题:

DKL(N(μ1,σ12)||N(μ2,σ22))=x12πσ1e(xμ1)22σ12log12πσ1e(xμ1)22σ1212πσ2e(xμ2)22σ22dx=x12πσ1e(xμ1)22σ12[logσ2σ1(xμ1)22σ12+(xμ2)22σ22]dx

第一项:

logσ2σ1x12πσ1e(xμ1)22σ12dx=logσ2σ1

第二项要看出里面是方差:

12σ12x(xμ1)212πσ1e(xμ1)22σ12dx=12σ12σ12=12

第三项,注意 E(x)2=D(x)+E(x2)

12σ22x(xμ2)212πσ1e(xμ1)22σ12dx=12σ22x(x22μ2x+μ22)12πσ1e(xμ1)22σ12dx=σ12+μ122μ1μ2+μ222σ22=σ12+(μ1μ2)22σ22

综上:

DKL(N(μ1,σ12)||N(μ2,σ22))=logσ2σ112+σ12+(μ1μ2)22σ22

二、组合题(图论)

有 n 个人,每次坐成一圈,为了使每次每个人的邻居与之前都不同,则坐法最多有几次?

实际上可以抽象为:完全图 Kn 中最多有多少个边不重复的哈密顿圈。

很明显,要将每个人都认识一遍且不重复,不会超过 n12 次,主要是考虑如何构造 [2]

奇数:

可以旋转 n12

偶数:

可以旋转 n22

故,答案为 n12

三、编程题(求编辑距离)

原题:72. 编辑距离 - 力扣

经典的字符串 dp 题:

int minDistance(string word1, string word2) {
    int m = word1.size(), n = word2.size();
    vector<vector<int>> dp(m + 1, vector<int>(n + 1));
    // dp[i][j] = max(
    //		dp[i-1][j] + 1, 	删除
    // 		dp[i][j-1] + 1, 	插入
    //		dp[i-1][j-1] + 1, 	修改
    //	)
    for (int i = 0; i <= m; i++) dp[i][0] = i;
    for (int j = 0; j <= n; j++) dp[0][j] = j;
    for (int i = 1; i <= m; i++) {
        for(int j = 1; j <= n; j++) {
            dp[i][j] = min(dp[i-1][j] + 1, dp[i][j-1] + 1);
            if (word1[i - 1] == word2[j - 1]) {
                dp[i][j] = min(dp[i][j], dp[i-1][j-1]);
            }
            else {
                dp[i][j] = min(dp[i][j], dp[i-1][j-1] + 1);
            }
        }
    }
    return dp[m][n];
}

四、编程题(滑动窗口)

原题:239. 滑动窗口最大值 - 力扣

主要考虑如果 i > j,并且 nums[i] > nums[j],则 j 就可以丢弃,故维护一个单调队列:

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
    int n = nums.size();
    deque<int> d;
    vector<int> ans;
    for(int i = 0; i < n; i++) {
        while(!d.empty() && nums[d.back()] <= nums[i]) d.pop_back();
        d.push_back(i);
        if(i >= k - 1) {
            while(!d.empty() && d.front() <= i - k) d.pop_front();
            ans.push_back(nums[d.front()]);
        }
    }
    return ans;
}

  1. 一篇文章讲清楚交叉熵和KL散度 - 知乎 (zhihu.com) ↩︎

  2. 11个人坐一圆桌儿,每次每人左右边两人都不同,问有几种坐法? ↩︎